/**
 * This file is part of Megasquirt Logger for PalmOS.
 * Copyright (C) 2011 Andrew Beard
 *
 * Megasquirt Logger is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Megasquirt Logger is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Megasquirt Logger.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "ExportDatabase.h"

#include "LogFile.h"

/**
 * Constructor for the ExportDatabase class.
 *
 * @param[in] databaseNumber The LocalID of the database to export.
 */
ExportDatabase::ExportDatabase( LocalID databaseNumber ) : 
    m_DatabaseNumber( databaseNumber ),
    m_nIndexOfPrimaryBuffer( 0 ),
    m_nBytesInPrimaryBuffer( 0 ),
    m_nBytesInSecondaryBuffer( 0 )
{
    m_LastError = DmDatabaseInfo( 0,
                                  m_DatabaseNumber,
                                  m_DatabaseName,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL );

    m_Database = DmOpenDatabase( 0,
                                 m_DatabaseNumber,
                                 dmModeReadOnly );
}

/**
 * Destructor for the ExportDatabase class.
 */
ExportDatabase::~ExportDatabase()
{
    if ( m_Database )
    {
        m_LastError = DmCloseDatabase( m_Database );
    }
}

/**
 * Writes the current database as a file in the filesystem.
 *
 * @param[in] nRealTimeUpdateLength The length of an output record from the
 *                                    MegaSquirt in bytes.
 */
void ExportDatabase::WriteToFilesystem( UInt16 nRealTimeUpdateLength )
{
    MemHandle databaseRecord;
    UInt32    nNumRecords    = 0;
    UInt8*    pRecordData    = NULL;
    LogFile*  pLogFile       = NULL;
    UInt16    nRecordSize    = 0;

    m_nIndexOfPrimaryBuffer   = 0;
    m_nBytesInPrimaryBuffer   = 0;
    m_nBytesInSecondaryBuffer = 0;

    pLogFile = new LogFile( m_DatabaseName );

    m_LastError = DmDatabaseSize( 0,
                                  m_DatabaseNumber,
                                  &nNumRecords,
                                  NULL,
                                  NULL );

    for ( UInt32 i = 0; i < nNumRecords; i++ )
    {
        databaseRecord = DmGetRecord( m_Database,
                                      i );

        if ( databaseRecord == NULL )
        {
            m_LastError = DmGetLastErr();
            return;
        }

        pRecordData = (UInt8*)MemHandleLock( databaseRecord );

        if ( i == 0 )
        {
            nRecordSize = LogFile::LOG_HEADER_LENGTH;
        }
        else
        {
            switch ( pRecordData[0] )
            {
                case LogFile::OUTPUT_RECORD:
                {
                    nRecordSize = sizeof(LogFile::T_RecordHeaderType) + nRealTimeUpdateLength;
                    break;
                }
                case LogFile::MARKER_RECORD:
                {
                    nRecordSize = sizeof(LogFile::T_RecordHeaderType);
                    break;
                }
                default:
                {
                    nRecordSize = 0;
                    break;
                }
            }
        }

        CopyToBuffer( pRecordData,
                      nRecordSize );

        m_LastError = MemHandleUnlock( databaseRecord );

        m_LastError = DmReleaseRecord( m_Database,
                                       i,
                                       false );

        // Check if we need to write a buffer of data
        if ( m_nBytesInPrimaryBuffer >= COPY_BUFFER_SIZE )
        {
            // Time to write to the card
            pLogFile->WriteData( m_CopyBuffers[ m_nIndexOfPrimaryBuffer ],
                                 m_nBytesInPrimaryBuffer );

            // Now swap the buffers
            m_nIndexOfPrimaryBuffer   = (m_nIndexOfPrimaryBuffer + 1) & 0x01;
            m_nBytesInPrimaryBuffer   = m_nBytesInSecondaryBuffer;
            m_nBytesInSecondaryBuffer = 0;
        }

    }

    /*
     * Check if we have remaining data to flush.  There should be no data in
     *   the secondary (since we just checked at the end of the loop) but there
     *   could be data in the primary.
     */
    if ( m_nBytesInPrimaryBuffer >= COPY_BUFFER_SIZE )
    {
        // Time to write to the card
        pLogFile->WriteData( m_CopyBuffers[ m_nIndexOfPrimaryBuffer ],
                             m_nBytesInPrimaryBuffer );
    }
    delete pLogFile;
}

/**
 * Copies the given data into the set of local buffers.
 *
 * @param[in] pData The data to store.
 * @param[in] nLength The number of bytes to store.
 */
void ExportDatabase::CopyToBuffer( UInt8* pData,
                                   UInt16 nLength )
{
    UInt16 nSpaceInPrimary = COPY_BUFFER_SIZE - m_nBytesInPrimaryBuffer;

    if ( nLength <= nSpaceInPrimary )
    {
        // There's enough space in the primary buffer.
        for ( UInt16 i = 0; i < nLength; i++ )
        {
            m_CopyBuffers[m_nIndexOfPrimaryBuffer][m_nBytesInPrimaryBuffer + i] = pData[i];
        }
        m_nBytesInPrimaryBuffer += nLength;
    }
    else
    {
        // We need to overflow to the secondary buffer
        UInt8 nIndexOfSecondaryBuffer = (m_nIndexOfPrimaryBuffer + 1) & 0x01;

        // Copy the stuff that will fit in the primary first
        for ( UInt16 i = 0; i < nSpaceInPrimary; i++ )
        {
            m_CopyBuffers[m_nIndexOfPrimaryBuffer][m_nBytesInPrimaryBuffer + i] = pData[i];
        }

        // Copy the remaining stuff into the secondary
        for ( UInt16 i = nSpaceInPrimary; i < nLength; i++ )
        {
            m_CopyBuffers[nIndexOfSecondaryBuffer][i - nSpaceInPrimary] = pData[i];
        }

        m_nBytesInPrimaryBuffer  += nSpaceInPrimary;
        m_nBytesInSecondaryBuffer = nLength - nSpaceInPrimary;
    }
}

/**
 * Returns the last error encountered by the object.
 *
 * @return The PalmOS error code for the last error encountered.
 */
Err ExportDatabase::GetLastError( void ) const
{
    return m_LastError;
}